package com.nuoniu.sibanyun.service.erp.in.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.entity.dto.VInventoryCheckMainSearchDto;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.erp.ErpPeriod;
import com.nuoniu.sibanyun.entity.erp.in.VInventoryCheckDetail;
import com.nuoniu.sibanyun.entity.erp.in.VInventoryCheckMain;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.entity.inOut.VInOutRecordDetail;
import com.nuoniu.sibanyun.entity.inOut.VInOutRecordMain;
import com.nuoniu.sibanyun.mapper.erp.ErpNumberSetMapper;
import com.nuoniu.sibanyun.mapper.erp.in.VInventoryCheckMainMapper;
import com.nuoniu.sibanyun.service.crm.ICrmGoodsService;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.erp.IErpPeriodService;
import com.nuoniu.sibanyun.service.erp.in.IInInventoryLocationService;
import com.nuoniu.sibanyun.service.erp.in.IInWarehouseService;
import com.nuoniu.sibanyun.service.erp.in.IVInventoryCheckDetailService;
import com.nuoniu.sibanyun.service.erp.in.IVInventoryCheckMainService;
import com.nuoniu.sibanyun.service.inOut.IVInOutRecordMainService;
import com.nuoniu.sibanyun.service.msg.ISysMsgService;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2021-05-19
 */
@Service
public class VInventoryCheckMainServiceImpl extends ServiceImpl<VInventoryCheckMainMapper, VInventoryCheckMain> implements IVInventoryCheckMainService {

    @Autowired
    private IErpNumberSetService numberSetService;
    @Autowired
    private ErpNumberSetMapper numberSetMapper;
    @Autowired
    private IErpAccountService accountService;
    @Autowired
    private IVInventoryCheckDetailService ivInventoryCheckDetailService;
    @Autowired
    private IVInOutRecordMainService ivInOutRecordMainService;
    @Autowired
    private IInWarehouseService iInWarehouseService;
    @Autowired
    private IInInventoryLocationService inInventoryLocationService;
    @Autowired
    private IErpPeriodService erpPeriodService;
    @Autowired
    private ICrmGoodsService crmGoodsService;
    @Autowired
    private ISysMsgService msgService;


    @Override
    public VInventoryCheckMain getOneByCondition(Integer actionType, Long currentId,boolean isLossOrGain) {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        VInventoryCheckMain inventoryCheckMain = null;
            switch (actionType){
            case 1:
                if(null != currentId){
                    inventoryCheckMain = baseMapper.getPreId(userInfo.getCompanyId(), currentAccountId,currentId,isLossOrGain);break;
                }else{
                    inventoryCheckMain = baseMapper.getFirstId(userInfo.getCompanyId(), currentAccountId,isLossOrGain);break;
                }
            case 2:
                if(null != currentId){
                    inventoryCheckMain = baseMapper.getNextId(userInfo.getCompanyId(), currentAccountId,currentId,isLossOrGain);break;
                }else{
                    inventoryCheckMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId,isLossOrGain);break;
                }
            case 4: inventoryCheckMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId,isLossOrGain);break;
            case 3: inventoryCheckMain = baseMapper.getFirstId(userInfo.getCompanyId(), currentAccountId,isLossOrGain);break;
            default:
                if(null != currentId){
                    inventoryCheckMain = getById(currentId);break;
                }else{
                    inventoryCheckMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId,isLossOrGain);break;
                }
        }
        if (null != inventoryCheckMain ){
            List<VInventoryCheckDetail> vInventoryCheckDetails = ivInventoryCheckDetailService.orderList(inventoryCheckMain.getId(), isLossOrGain);
            inventoryCheckMain.setOrderDetailList(vInventoryCheckDetails);
        }

        return inventoryCheckMain;
    }

    @Override
    public Page<VInventoryCheckMain> pageVInventoryCheckMain(Page page, VInventoryCheckMainSearchDto inventoryCheckMainSearchDto) {
        UserInfo userInfo = UserThreadLocal.get();
        Page<VInventoryCheckMain> vInventoryCheckMainPage = baseMapper.pageVInventoryCheckMain(page, userInfo.getCompanyId(), inventoryCheckMainSearchDto);
        return vInventoryCheckMainPage;
    }

    @Override
    public String getCode() {
        UserInfo userInfo = UserThreadLocal.get();
        ErpNumberSet numberSet = numberSetMapper.getByCode("InventoryCheck",userInfo.getCompanyId(), accountService.getCurrentAccountId());
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getCode(userInfo.getCompanyId(), dateStr);
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    public void saveOrder(VInventoryCheckMain vInventoryCheckMain) {
        LocalDateTime now = LocalDateTime.now();
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        List<VInventoryCheckDetail> orderDetailList = vInventoryCheckMain.getOrderDetailList();
        if( null == orderDetailList || orderDetailList.size() == 0)throw new NuoNiuException("明细不能为空");
        /****************  仓库权限校验 ——开始   ********************/
        Integer warehouseId = vInventoryCheckMain.getWarehouseId();
        boolean isHaveOperationRight = iInWarehouseService.isHaveOperationRight(warehouseId.longValue());
        if(!isHaveOperationRight)throw new NuoNiuException("你没有权限新增该仓库的盘点表");
        /****************  仓库权限校验 ——结束   ********************/
        Long mainId = vInventoryCheckMain.getId();
        if (null == mainId){
            String code = getCode();
            if(code == null) {
                throw new NuoNiuException("单据编号已上限");
            }
            boolean isHaveId = orderDetailList.stream().anyMatch(f -> null != f.getId());
            if(isHaveId)throw new NuoNiuException("单据明细错误：不能新增已经存在的明细记录");
            vInventoryCheckMain.setVoucherCode(code);
            vInventoryCheckMain.setAccountId(currentAccountId);
            vInventoryCheckMain.setCompanyId(userInfo.getCompanyId());
            vInventoryCheckMain.setCreateUserId(userInfo.getUserId());
            vInventoryCheckMain.setCreateUserName(userInfo.getRealName());
            vInventoryCheckMain.setCreateDate(now);
        }else{
            //校验详情（详情不能有其他单据的明细ID）
            boolean isHaveErrorId = orderDetailList.stream().anyMatch(f -> null != f.getOrderId() && !mainId.toString().equals(f.getOrderId().toString()));
            if(isHaveErrorId)throw new NuoNiuException("单据明细错误：不能修改其他单据的明细记录");
            VInventoryCheckMain oldVInventoryCheckMain = this.getById(mainId);
            Integer voucherState = oldVInventoryCheckMain.getVoucherState();
            if(voucherState != null && voucherState > 0) {
                throw new NuoNiuException("已审核,不能修改");
            }
            /**************  修改权限 *******************/
            if(!crmGoodsService.isHaveOperationRight(oldVInventoryCheckMain.getCreateUserId().longValue())){
                throw new NuoNiuException("你没有权限操作该记录");
            }
        }
        boolean haveInInventoryLocation = iInWarehouseService.isHaveInInventoryLocation(warehouseId.longValue());
        if(haveInInventoryLocation){
            List<Integer> inventoryLocationIds = inInventoryLocationService.getByWarehouseId(warehouseId);
            if(inventoryLocationIds.size() == 0)throw new NuoNiuException("请设置当前仓库的货位！");
            boolean isLocationIdNull = orderDetailList.stream().anyMatch(f -> null == f.getInventoryLocationId() || !inventoryLocationIds.contains(f.getInventoryLocationId()));
            if(isLocationIdNull)throw new NuoNiuException("请选择正确的货位！");
        }
        //设置 账套年份 和 账套期间
        vInventoryCheckMain.setAccountYear(now.getYear());
        ErpPeriod erpPeriod = erpPeriodService.checkErpPeriodByDate(now.toLocalDate(),"1");
        if(erpPeriod == null){
            throw new NuoNiuException("未结转下年，请到财务系统结转下年");
        }
        vInventoryCheckMain.setAccountMonth(erpPeriod.getCurrentPeriod());
        //保存单据主表
        boolean result = this.saveOrUpdate(vInventoryCheckMain);

        if(result) {
            List<Long> detailIds = new ArrayList<>();
            orderDetailList.forEach(p -> {
                if(null == p.getBaseQuantity())p.setBaseQuantity(BigDecimal.ZERO);
                BigDecimal lossOrGainQuantity = p.getCheckQuantity().subtract(p.getBaseQuantity());
                p.setLossOrGainQuantity(lossOrGainQuantity);
                if(null != p.getPrice()){
                    BigDecimal lossOrGainAmount = lossOrGainQuantity.multiply(p.getPrice());
                    p.setLossOrGainAmount(lossOrGainAmount);
                }
                p.setOrderId(vInventoryCheckMain.getId().intValue());
                p.setCompanyId(userInfo.getCompanyId());
                p.setAccountId(vInventoryCheckMain.getAccountId());
                p.setAccountYear(vInventoryCheckMain.getAccountYear());
                p.setWarehouseId(vInventoryCheckMain.getWarehouseId());
                p.setWarehouseName(vInventoryCheckMain.getWarehouseName());
                p.setWarehouseCode(vInventoryCheckMain.getWarehouseCode());
                if(p.getId() != null) {
                    detailIds.add(p.getId());
                }
            });

            //删除明细
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("order_id", mainId);
            if(detailIds.size() > 0) {
                wrapper.notIn("id", detailIds);
                ivInventoryCheckDetailService.remove(wrapper);
            }else {
                ivInventoryCheckDetailService.remove(wrapper);
            }
            //保存单据明细表
            ivInventoryCheckDetailService.saveOrUpdateBatch(orderDetailList);
        }
    }

    /**
     * 单据复核
     */
    @Override
    public void firstExamine(Long id, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        if ( !(0 == status || 1 == status))throw new NuoNiuException("参数错误");
        VInventoryCheckMain inventoryCheckMain = getById(id);
        if (null == inventoryCheckMain)throw new NuoNiuException("查询不到记录");
        Integer voucherState = inventoryCheckMain.getVoucherState();
        UpdateWrapper<VInventoryCheckMain> updateWrapper = new UpdateWrapper();
        //校验状态
        if (!( 0 == status && 1 == voucherState) && !(1 == status && 0 == voucherState)) throw new NuoNiuException("单据不能复核或者取消复核");

        updateWrapper.eq("id",id);
        updateWrapper.set("voucher_state",status);
        updateWrapper.set("reviewer_user_id",userInfo.getUserId());
        updateWrapper.set("reviewer_user_name",userInfo.getRealName());
        update(updateWrapper);

        if(status.equals(1)) { //盘点消息通知
            msgService.checkMsgNotice(inventoryCheckMain);
        }
    }
    /**
     * 单据监盘
     * @param id
     */
    @Override
    public void secondExamine(Long id, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        if ( !(1 == status || 2 == status))throw new NuoNiuException("参数错误");
        VInventoryCheckMain inventoryCheckMain = getById(id);
        if (null == inventoryCheckMain)throw new NuoNiuException("查询不到记录");
        Integer voucherState = inventoryCheckMain.getVoucherState();
        UpdateWrapper<VInventoryCheckMain> updateWrapper = new UpdateWrapper();
        //校验状态
        if (!( 1 == status && 2 == voucherState) && !(2 == status && 1 == voucherState)) throw new NuoNiuException("单据不能监盘或者取消监盘");
        Integer createUserId = inventoryCheckMain.getCreateUserId();
//        if(createUserId.equals(userInfo.getUserId()))throw new NuoNiuException("监盘不能是同一个人");
        updateWrapper.eq("id",id);
        updateWrapper.set("voucher_state",status);
        updateWrapper.set("supervise_user_id",userInfo.getUserId());
        updateWrapper.set("supervise_user_name",userInfo.getRealName());

        update(updateWrapper);

        //消息通知
        if(status.equals(2)) { //盘点消息通知
            msgService.checkMsgNotice(inventoryCheckMain);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void thirdExamine(Long id, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        if ( 3 != status)throw new NuoNiuException("参数错误");
        VInventoryCheckMain inventoryCheckMain = getById(id);
        if (null == inventoryCheckMain)throw new NuoNiuException("查询不到记录");
        Integer voucherState = inventoryCheckMain.getVoucherState();
        if(voucherState > 2)throw new NuoNiuException("已经批准");
        UpdateWrapper<VInventoryCheckMain> updateWrapper = new UpdateWrapper();
        /** 自动生成 其他出入库单据（确认入库） */
        autoGenerateOtherInOut(inventoryCheckMain);

        updateWrapper.eq("id",id);
        updateWrapper.set("voucher_state",status);
        updateWrapper.set("approve_user_id",userInfo.getUserId());
        updateWrapper.set("approve_user_name",userInfo.getRealName());
        update(updateWrapper);

    }

    @Override
    public void delete(Long id) {
        VInventoryCheckMain inventoryCheckMain = getById(id);
        Integer voucherState = inventoryCheckMain.getVoucherState();
        if (voucherState > 1)throw new NuoNiuException("已经复核，不能删除");
        /****************  仓库权限校验 ——开始   ********************/
        Integer warehouseId = inventoryCheckMain.getWarehouseId();
        boolean isHaveOperationRight = iInWarehouseService.isHaveOperationRight(warehouseId.longValue());
        if(!isHaveOperationRight)throw new NuoNiuException("你没有权限新增该仓库的盘点表");
        /****************  仓库权限校验 ——结束   ********************/
        removeById(id);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void autoGenerateOtherInOut(VInventoryCheckMain inventoryCheckMain) {
        LocalDate now = LocalDate.now();
        Boolean isGenerateOtherInOut = inventoryCheckMain.getIsGenerateOtherInOut();
        if(isGenerateOtherInOut)throw new NuoNiuException("已经生成其他入库");
        Long checkMainId = inventoryCheckMain.getId();
        //1.查询 盘点业务 盈亏明细
        List<VInventoryCheckDetail> vInventoryCheckDetails = ivInventoryCheckDetailService.orderList(checkMainId,true);
        if (null == vInventoryCheckDetails || vInventoryCheckDetails.size() == 0)throw new NuoNiuException("查询不到盈亏数据");

        VInOutRecordMain inRecordMain = new VInOutRecordMain();
        inRecordMain.setVoucherDate(now);
        inRecordMain.setBusinessType(101);      // 101 盘点入库业务 102 盘点出库业务
        inRecordMain.setReceiveDeliverType(1);  // 1.收 2.发
        inRecordMain.setWarehouseId(inventoryCheckMain.getWarehouseId());
        inRecordMain.setWarehouseName(inventoryCheckMain.getWarehouseName());
        inRecordMain.setWarehouseCode(inventoryCheckMain.getWarehouseCode());
        inRecordMain.setSourceVoucherId(checkMainId.intValue());
        inRecordMain.setSourceVoucherType("InventoryCheck");
        inRecordMain.setDataSource("OtherReceiveVoucher");
        List<VInOutRecordDetail> inOrderDetailList = new ArrayList<>();
        inRecordMain.setOrderDetailList(inOrderDetailList);


        VInOutRecordMain outRecordMain = new VInOutRecordMain();
        outRecordMain.setVoucherDate(now);
        outRecordMain.setBusinessType(102);      // 101 盘点入库业务 102 盘点出库业务
        outRecordMain.setReceiveDeliverType(2);  // 1.收 2.发
        outRecordMain.setWarehouseId(inventoryCheckMain.getWarehouseId());
        outRecordMain.setWarehouseName(inventoryCheckMain.getWarehouseName());
        outRecordMain.setWarehouseCode(inventoryCheckMain.getWarehouseCode());
        outRecordMain.setSourceVoucherId(checkMainId.intValue());
        outRecordMain.setSourceVoucherType("InventoryCheck");
        outRecordMain.setDataSource("OtherDispatchVoucher");
        List<VInOutRecordDetail> outOrderDetailList = new ArrayList<>();
        outRecordMain.setOrderDetailList(outOrderDetailList);

        vInventoryCheckDetails.forEach(detail->{
            BigDecimal lossOrGainQuantity = detail.getLossOrGainQuantity();
            VInOutRecordDetail vInOutRecordDetail = new VInOutRecordDetail();
            BeanUtils.copyProperties(detail,vInOutRecordDetail);
            vInOutRecordDetail.setSourceDetailId(detail.getId().intValue());
            vInOutRecordDetail.setId(null);
            vInOutRecordDetail.setBaseQuantity(detail.getLossOrGainQuantity().abs());
            if( null != detail.getLossOrGainAmount()){
                vInOutRecordDetail.setAmount(detail.getLossOrGainAmount().abs());
            }else{
                detail.setLossOrGainAmount(BigDecimal.ZERO);
            }
            if (lossOrGainQuantity.compareTo(BigDecimal.ZERO) > 0){
                inOrderDetailList.add(vInOutRecordDetail);
            }else if(lossOrGainQuantity.compareTo(BigDecimal.ZERO) < 0){
                outOrderDetailList.add(vInOutRecordDetail);
            }
        });


        if (inOrderDetailList.size() > 0){
            ivInOutRecordMainService.saveOrder(inRecordMain,2);
            ivInOutRecordMainService.confirm(inRecordMain.getId(),2,false);
        }
        if (outOrderDetailList.size() > 0){
            ivInOutRecordMainService.saveOrder(outRecordMain,2);
            ivInOutRecordMainService.confirm(outRecordMain.getId(),2,false);
        }

        // 更新 盘点业务表 '是否生成其他出入库单据' 状态
        baseMapper.updateIsGenerateOtherInOut(inventoryCheckMain.getId(),true);


    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void autoDeleteOtherInOut(VInventoryCheckMain inventoryCheckMain) {
        LocalDate now = LocalDate.now();
        Boolean isGenerateOtherInOut = inventoryCheckMain.getIsGenerateOtherInOut();
        if(!isGenerateOtherInOut)throw new NuoNiuException("还未生成其他入库");
        Long checkMainId = inventoryCheckMain.getId();
        List<VInventoryCheckDetail> vInventoryCheckDetails = ivInventoryCheckDetailService.orderList(checkMainId,true);
        if (null == vInventoryCheckDetails || vInventoryCheckDetails.size() == 0)throw new NuoNiuException("查询不到盈亏数据");
        // todo 删除 已确认 的其他出入库流水 并 重新计算 库存情况






        // 更新 盘点业务表 '是否生成其他出入库单据' 状态
        baseMapper.updateIsGenerateOtherInOut(inventoryCheckMain.getId(),false);
    }

    @Override
    public void checkOtherInOutFromInventoryCheck(VInventoryCheckMain inventoryCheckMain) {

    }
}
